import urllib.parse
import logging
from fastapi import APIRouter, HTTPException, Depends, Request
from fastapi.responses import RedirectResponse, JSONResponse
import httpx
import jwt
from datetime import datetime, timedelta
from config.dingtalk_config import DINGTALK_OAUTH_CONFIG
from factories.social_login_factory import SocialLoginFactory
from providers.dingtalk_provider import DingtalkProvider

# 注册钉钉提供者
SocialLoginFactory.register_provider("dingtalk", DingtalkProvider)

# 配置日志
logging.basicConfig(level=logging.INFO)
logger = logging.getLogger(__name__)

dingtalk_router = APIRouter()

# JWT密钥，实际项目中应该从环境变量获取
SECRET_KEY = "your-secret-key-please-change-in-production"
ALGORITHM = "HS256"

def create_access_token(data: dict, expires_delta: timedelta = None):
    """创建JWT访问令牌"""
    to_encode = data.copy()
    if expires_delta:
        expire = datetime.utcnow() + expires_delta
    else:
        expire = datetime.utcnow() + timedelta(minutes=15)
    to_encode.update({"exp": expire})
    encoded_jwt = jwt.encode(to_encode, SECRET_KEY, algorithm=ALGORITHM)
    return encoded_jwt

@dingtalk_router.get("/dingtalk/login")
async def dingtalk_login():
    """重定向到钉钉登录页面"""
    #工厂模式好处：当系统需要从 "微信登录" 切换为 "支付宝登录" 时，新增一个登录方法类，无需改动登录后的业务处理逻辑，极大提升了代码的灵活性和可维护性
    try:
        # 使用工厂模式创建钉钉提供者
        provider = SocialLoginFactory.create_provider("dingtalk", DINGTALK_OAUTH_CONFIG)
        if not provider:
            return JSONResponse(status_code=500, content={"detail": "无法创建钉钉登录提供者"})
        
        redirect_url = await provider.get_authorization_url()
        return RedirectResponse(url=redirect_url)
    except Exception as e:
        logger.error(f"钉钉登录重定向失败: {str(e)}")
        return JSONResponse(status_code=500, content={"detail": "钉钉登录重定向失败"})

@dingtalk_router.get("/user/dingtalkCallback/")
async def dingtalk_callback(request: Request, code: str = None):
    """钉钉登录回调处理"""
    try:
        if not code:
            logger.warning("缺少授权码")
            return JSONResponse(status_code=400, content={"detail": "缺少授权码"})
        
        # 使用工厂模式创建钉钉提供者
        provider = SocialLoginFactory.create_provider("dingtalk", DINGTALK_OAUTH_CONFIG)
        if not provider:
            return JSONResponse(status_code=500, content={"detail": "无法创建钉钉登录提供者"})
        
        # 获取访问令牌
        token_response = await provider.exchange_code_for_token(code)
        
        if token_response["status_code"] != 200:
            logger.error(f"获取访问令牌失败: {token_response.get('text', '')}")
            return JSONResponse(status_code=token_response["status_code"], content={"detail": "获取访问令牌失败"})
        
        token_data = token_response["data"]
        access_token = token_data.get("accessToken")
        
        if not access_token:
            logger.error("未获取到accessToken")
            return JSONResponse(status_code=400, content={"detail": "未获取到accessToken"})
        
        # 获取用户信息
        user_info = await provider.get_user_info(access_token)
        
        if "errcode" in user_info and user_info["errcode"] != 0:
            logger.error(f"获取用户信息失败: {user_info}")
            return JSONResponse(status_code=400, content={"detail": "获取用户信息失败"})
        
        # 保存用户信息
        dingtalk_user = await provider.save_user_info(user_info, token_data)
        
        # 创建JWT令牌
        access_token = create_access_token(
            data={
                "sub": str(dingtalk_user.id),
                "nickname": dingtalk_user.nickname,
                "avatar": dingtalk_user.avatar_url
            },
            expires_delta=timedelta(days=7)
        )
        
        # 重定向到前端，携带token
        frontend_redirect_url = f"http://localhost:5173/login?token={access_token}&user_id={dingtalk_user.id}"
        return RedirectResponse(url=frontend_redirect_url)
    
    except Exception as e:
        logger.error(f"钉钉登录回调处理失败: {str(e)}")
        return JSONResponse(status_code=500, content={"detail": "钉钉登录处理失败"})

async def _get_access_token(auth_code: str):
    """
    使用授权码换取访问令牌
    """
    token_url = DINGTALK_OAUTH_CONFIG["token_url"]
    
    payload = {
        "clientId": DINGTALK_OAUTH_CONFIG["client_id"],
        "clientSecret": DINGTALK_OAUTH_CONFIG["client_secret"],
        "code": auth_code,
        "grantType": "authorization_code"
    }
    
    headers = {
        "Content-Type": "application/json",
        "Accept": "application/json"
    }
    
    try:
        print(f"Requesting token with payload: {payload}")
        async with httpx.AsyncClient() as client:
            response = await client.post(token_url, json=payload, headers=headers, timeout=10.0)
            print(f"Token request response - Status: {response.status_code}, Content: {response.text}")
            # 返回完整的响应信息，包括状态码和内容
            return {
                "status_code": response.status_code,
                "data": response.json() if response.content else {},
                "text": response.text
            }
    except Exception as e:
        print(f"Token request exception: {str(e)}")
        return {
            "status_code": 500,
            "data": {},
            "text": f"请求异常: {str(e)}"
        }

async def _get_user_info(access_token: str):
    """
    使用访问令牌获取用户信息
    """
    userinfo_url = DINGTALK_OAUTH_CONFIG["userinfo_url"]
    
    headers = {
        "x-acs-dingtalk-access-token": access_token
    }
    
    try:
        print(f"Requesting user info with access_token: {access_token}")
        async with httpx.AsyncClient() as client:
            response = await client.get(userinfo_url, headers=headers, timeout=10.0)
            print(f"User info response - Status: {response.status_code}, Content: {response.text}")
            return response.json()
    except Exception as e:
        print(f"User info request exception: {str(e)}")
        return {"error": f"获取用户信息异常: {str(e)}"}